﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JustMathCore.AddrToolCoin
{
    class NEO3 : AddrTools.IAddrConv
    {
        public AddrTools.AddrType Type
        {
            get
            {
                return AddrTools.AddrType.NEO3;
            }
        }
        public byte[] Addr2Hash(string addr)
        {
            if (addr.Length == 34)
            {
                return AddrTools.base58Decode(addr).Skip(1).Take(20).ToArray();
            }
            else if (addr.Length == 40)
            {
                byte[] output = new byte[20];
                HexTool.Hexstr2Bytes(addr, output);
                return output;
            }
            else
            {
                throw new Exception("error address");
            }
        }

        //public void Addr2Hash(string addr, byte[] outdata, int outpos)
        //{
        //    var data = AddrTools.base58Decode(addr).Skip(1).Take(20).ToArray();
        //    for (var i = 0; i < 20; i++)
        //    {
        //        outdata[i + outpos] = data[i];
        //    }
        //}

        public string GetAddr(byte[] prikey)
        {
            var scriptHash = GetHashAddr(prikey);

            return Hash2Addr(scriptHash);

        }

        public byte[] GetHashAddr(byte[] prikey)
        {
            if (curve == null)
                curve = Org.BouncyCastle.Crypto.EC.CustomNamedCurves.GetByName("secp256r1");

            var g = curve.G;
            var k = new Org.BouncyCastle.Math.BigInteger(1, prikey);
            var pubkey = g.Multiply(k).GetEncoded(true);
            if (pubkey.Length == 1)
            {
                byte[] scriptHasherror = GetScriptHashFromPublicKey(pubkey);
                return scriptHasherror;
            }
            //33
           
            if (bufscript == null)
            {
                bufscript = new byte[pubkey.Length + 7];
                bufscript[0] = 0x0c;// (byte)pubkey.Length;
                bufscript[1] = 0x21;// (byte)pubkey.Length;
                bufscript[bufscript.Length - 5] = 0x41;// 172;//c
                bufscript[bufscript.Length - 4] = 0x56;
                bufscript[bufscript.Length - 3] = 0xe7;
                bufscript[bufscript.Length - 2] = 0xb3;
                bufscript[bufscript.Length - 1] = 0x27;

            }
            for (var i = 0; i < pubkey.Length; i++)
                bufscript[i + 2] = pubkey[i];

            var scripthash256 = NBitcoin.Crypto.Hashes.SHA256(bufscript);
            var scriptHash = NBitcoin.Crypto.Hashes.RIPEMD160(scripthash256);

            return scriptHash;
        }

        public string GetWif(byte[] prikey)
        {
            if (prikey.Length != 32)
                throw new Exception("error prikey.");
            byte[] data = new byte[34];
            data[0] = 0x80;
            data[33] = 0x01;
            for (var i = 0; i < 32; i++)
            {
                data[i + 1] = prikey[i];
            }
            byte[] checksum = NBitcoin.Crypto.Hashes.SHA256(data);
            checksum = NBitcoin.Crypto.Hashes.SHA256(checksum);
            checksum = checksum.Take(4).ToArray();
            byte[] alldata = data.Concat(checksum).ToArray();
            string wif = AddrTools.base58Encode(alldata);
            return wif;
        }

        public string Hash2Addr(byte[] scriptHash)
        {
            byte[] data = new byte[20 + 1 + 4];
            data[0] = 0x35;
            for (var i = 0; i < scriptHash.Length; i++)
            {
                data[i + 1] = scriptHash[i];
            }
            var hash = NBitcoin.Crypto.Hashes.SHA256(data, 0, 21);
            hash = NBitcoin.Crypto.Hashes.SHA256(hash);

            data[21] = hash[0];
            data[22] = hash[1];
            data[23] = hash[2];
            data[24] = hash[3];

            var addrop = AddrTools.base58Encode(data);
            return addrop;
        }

        [ThreadStatic]
        static byte[] bufscript;

        [ThreadStatic]
        static Org.BouncyCastle.Asn1.X9.X9ECParameters curve;

        public static byte[] GetScriptHashFromPublicKey(byte[] publicKey)
        {
            byte[] script = new byte[publicKey.Length + 2];
            script[0] = (byte)publicKey.Length;
            Array.Copy(publicKey, 0, script, 1, publicKey.Length);
            script[script.Length - 1] = 172;//CHECKSIG

            var scripthash = NBitcoin.Crypto.Hashes.SHA256(script);
            scripthash = NBitcoin.Crypto.Hashes.RIPEMD160(scripthash);
            return scripthash;
        }

    }
}
